knitr::include_graphics("IMG_0667.JPG")
# Set chunk options to hide warnings, messages, and errors
knitr::opts_chunk$set(
warning = FALSE,
message = FALSE,
error = FALSE, # <- This hides error messages too
include = TRUE
)
library(tidyverse)
library(dplyr)
library(stringr)
library(easystats)
library(equatiomatic)
library(pander)
library(broom)
library(kableExtra)
library(rmdformats)
library(prettydoc)
library(hrbrthemes)
library(tint)
library(tufte)
library(flexdashboard)
dat <- read.csv("plate_reading_2.csv")
We will look at an MTT assay performed on COS-7 cells, which are cancerous monkey kidney cells, using different sterilization reagents. The chemicals used were water, ethanol, and hydrogen peroxide. Water was used to ensure that it wasn’t only the diluting of media that was causing cell death. Diluting media with water causes cells to not have as much nutrients so there is some expected cell death. Ethanol and Hydrogen peroxide are often used in the lab setting as sterilizing reagents, so we were curious to see what the effect on eukaryotic cells are. The standard which are the two first wells are to create a standard curve of “cell death” to compare the results to because the cell numbers within the ethanol and hydrogen peroxide all had 12,000 cells to see what the affect of the reagents were. So let’s take a look at the data!
print(dat)
## Well standard Cell_count standard.1 Cell_count.1 water Cell_count.2
## 1 A 0.560 12000 0.475 12000 0.204 12000
## 2 B 0.382 10000 0.404 10000 0.203 10000
## 3 C 0.454 8000 0.422 8000 0.283 8000
## 4 D 0.350 6000 0.326 6000 0.406 6000
## 5 E 0.153 4000 0.200 4000 0.679 4000
## 6 F 0.160 2000 0.155 2000 0.766 2000
## 7 G 0.141 1000 0.130 1000 0.681 1000
## 8 H 0.111 500 0.108 500 0.870 500
## Concentration water.1 Cell_count.3 Concentration.1 ethanol Cell_count.4
## 1 0.5000 0.138 12000 0.5000 0.102 12000
## 2 0.2500 0.154 10000 0.2500 0.104 12000
## 3 0.1250 0.207 8000 0.1250 0.105 12000
## 4 0.0625 0.219 6000 0.0625 0.119 12000
## 5 0.0312 0.404 4000 0.0312 0.263 12000
## 6 0.0156 0.572 2000 0.0156 0.494 12000
## 7 0.0078 0.694 1000 0.0078 0.590 12000
## 8 0.0039 0.803 500 0.0039 0.727 12000
## Concentration.2 ethanol.1 Cell_count.5 Concentration.3 hydrogen_peroxide
## 1 0.5000 0.108 12000 0.5000 0.106
## 2 0.2500 0.107 12000 0.2500 0.100
## 3 0.1250 0.103 12000 0.1250 0.103
## 4 0.0625 0.125 12000 0.0625 0.125
## 5 0.0312 0.158 12000 0.0312 0.158
## 6 0.0156 0.187 12000 0.0156 0.187
## 7 0.0078 0.312 12000 0.0078 0.312
## 8 0.0039 0.366 12000 0.0039 0.336
## Cell_count.6 Concentration.4 hydrogen_peroxide.1 Cell_count.7 Concentration.5
## 1 12000 0.0750 0.112 12000 0.0750
## 2 12000 0.0370 0.101 12000 0.0370
## 3 12000 0.0180 0.100 12000 0.0180
## 4 12000 0.0090 0.094 12000 0.0090
## 5 12000 0.0040 0.092 12000 0.0040
## 6 12000 0.0020 0.098 12000 0.0020
## 7 12000 0.0010 0.095 12000 0.0010
## 8 12000 0.0005 0.085 12000 0.0005
Looking at this data it is a little messy so we need to clean it up in order to properly perform any type of analysis on it! So what are the steps of cleaning data, well here they are!!
Cleaning data steps:
We have already imported our data, so let’s go ahead and clean it!
library(tidyverse)
library(broom)
library(dplyr)
library(bslib)
library(shiny)
dat <- read.csv("plate_reading_2.csv")
names(dat)
## [1] "Well" "standard" "Cell_count"
## [4] "standard.1" "Cell_count.1" "water"
## [7] "Cell_count.2" "Concentration" "water.1"
## [10] "Cell_count.3" "Concentration.1" "ethanol"
## [13] "Cell_count.4" "Concentration.2" "ethanol.1"
## [16] "Cell_count.5" "Concentration.3" "hydrogen_peroxide"
## [19] "Cell_count.6" "Concentration.4" "hydrogen_peroxide.1"
## [22] "Cell_count.7" "Concentration.5"
dat_1 <- dat %>%
pivot_longer(cols = starts_with("cell_count"),
names_to = "measurement",
values_to = "cell_count")
dat_1 <- dat %>%
rename_with(~ sub("//.//d+$", "", .x)) %>%
pivot_longer(cols = c("Cell_count", "Cell_count.1", "Cell_count.2",
"Cell_count.3", "Cell_count.4", "Cell_count.5",
"Cell_count.6", "Cell_count.7"),
names_to = "Measurement",
values_to = "cell_count")
dat_2 <- dat_1 %>%
rename_with(~ sub("//.//d+$", "", .x)) %>%
pivot_longer(cols = c("standard", "standard.1", "water", "water.1",
"ethanol", "ethanol.1", "hydrogen_peroxide",
"hydrogen_peroxide.1"),
names_to = "reagent",
values_to = "absorbance")
dat_3 <- dat_2 %>%
pivot_longer(cols = c("Concentration", "Concentration.1",
"Concentration.2", "Concentration.3",
"Concentration.4", "Concentration.5"),
names_to = "concentration.1",
values_to = "concentration")
dat_cleaned <- dat_3 %>%
select("Well",
"cell_count",
"reagent",
"absorbance",
"concentration")
Now let us look at the data in a table and put them into models.
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
theme = bs_theme(bootswatch = "minty"),
titlePanel("Final Project: Cell Death Data"),
tabsetPanel(
tabPanel("Collapsible Table",
actionButton("toggle_table", "Show/Hide Table"),
uiOutput("table_output")
),
tabPanel("Model Dashboard",
sidebarLayout(
sidebarPanel(
selectInput("model_choice", "Choose a Model:",
choices = c("Model 1: concentration ~ absorbance" = "mod_1",
"Model 2: concentration ~ concentration * absorbance" = "mod_2",
"Model 3: cell_count ~ absorbance" = "mod_3"),
selected = "mod_1"),
selectInput("reagent_filter", "Filter by Reagent:",
choices = c("All", "Water", "Ethanol", "Hydrogen Peroxide"),
selected = "All"),
checkboxInput("log_trendline", "Add Logarithmic Trendline", value = FALSE)
),
mainPanel(
tabsetPanel(
tabPanel("Model Summary", verbatimTextOutput("model_summary")),
tabPanel("Actual vs Predicted Plot", plotOutput("prediction_plot")),
tabPanel("Model Metrics", verbatimTextOutput("model_metrics"))
)
)
)
)
)
)
server <- function(input, output, session) {
# Collapsible Table
output$table_output <- renderUI({
req(input$toggle_table)
if (input$toggle_table %% 2 == 1) {
DT::dataTableOutput("table")
}
})
output$table <- DT::renderDataTable({
dat_cleaned
})
# Models
mod_1 <- glm(concentration ~ absorbance, data = dat_cleaned)
mod_2 <- glm(concentration ~ concentration * absorbance, data = dat_cleaned)
mod_3 <- glm(cell_count ~ absorbance, data = dat_cleaned)
model_lookup <- list(mod_1 = mod_1, mod_2 = mod_2, mod_3 = mod_3)
selected_model <- reactive({
model_lookup[[input$model_choice]]
})
output$model_summary <- renderPrint({
summary(selected_model())
})
output$prediction_plot <- renderPlot({
model <- selected_model()
preds <- predict(model)
actual <- model$y
ggplot(data.frame(actual = actual, predicted = preds),
aes(x = actual, y = predicted)) +
geom_point(alpha = 0.6, color = "#2c3e50") +
geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "red") +
labs(title = "Actual vs Predicted", x = "Actual", y = "Predicted") +
theme_minimal()
})
output$model_metrics <- renderPrint({
model <- selected_model()
preds <- predict(model)
actual <- model$y
rmse <- sqrt(mean((actual - preds)^2))
r2 <- cor(actual, preds)^2
cat("RMSE:", round(rmse, 3), "\n")
cat("R²:", round(r2, 3), "\n")
})
}
shinyApp(ui = ui, server = server)
After looking at these models let us compare them
dat_cleaned %>%
ggplot(aes(x = concentration,
y = absorbance,
color = reagent)) +
geom_point() +
facet_wrap(~reagent)
Here is a visible picture of the results
Looking at the image, there is a nice gradient of purple where the less of a reagent was used, the darker it was, which means that there are more cells present here because the mitochondria is converting formazan crystals to get that purple color. So as a visible cue the reagents kill off these cells. Looking at these graphs we can see that hydrogen peroxide will kill anything it touches. The surprising thing is that ethanol and water follow the same trend of cell amounts. This could be because the ethanol wasn’t concentrated enough, but we can contribute some of the cell death to the DMSO. The modeling dashboard provides insight into how absorbance relates to both concentration and cell count, with model comparisons guiding which has better predictive power.
Possible points of error could have been as we learned with our previous run to use parafilm to ensure that the ethanol isn’t going to evaporate off. Understanding that hydrogen peroxide is so strong, even at such low concentrations it killed off most cells so figuring out what we could do for another run. If we were to run this experiment again, we would dilute the hydrogen peroxide even more to get it so dilute to where cells are able to survive. Rerunning this experiment would be a great idea for the purpose of reproducing results to ensure that we just didn’t get a one time number rather our results would become more reliable. Maybe adding another reagent, or running with different reagents over all. Seeing the effects of the caffeine for example on kidney cells or how some vitamins affect kidney cells. Maybe using a different cell line to see what the difference is in monkey kidney cells versus say a rabbit or a rat’s kidney cells.